﻿// Decompiled with JetBrains decompiler
// Type: Commom.Util.ObjectId
// Assembly: MT5Trade, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 8045A2CB-DBD6-41AD-8804-F98CAC1BDC8D
// Assembly location: C:\projects\trade\trade_tool\trade0812\MT5Trade.dll

using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

#nullable enable
namespace Commom.Util;

internal struct ObjectId : IComparable<ObjectId>, IEquatable<ObjectId>
{
  private static readonly DateTime __unixEpoch;
  private static readonly long __dateTimeMaxValueMillisecondsSinceEpoch;
  private static readonly long __dateTimeMinValueMillisecondsSinceEpoch;
  private static ObjectId __emptyInstance = new ObjectId();
  private static int __staticMachine;
  private static short __staticPid;
  private static int __staticIncrement;
  private static uint[] _lookup32 = Enumerable.Range(0, 256 /*0x0100*/).Select<int, uint>((Func<int, uint>) (i =>
  {
    string str = i.ToString("x2");
    return (uint) str[0] + ((uint) str[1] << 16 /*0x10*/);
  })).ToArray<uint>();
  private int _timestamp;
  private int _machine;
  private short _pid;
  private int _increment;

  static ObjectId()
  {
    ObjectId.__unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    ObjectId.__dateTimeMaxValueMillisecondsSinceEpoch = (DateTime.MaxValue - ObjectId.__unixEpoch).Ticks / 10000L;
    ObjectId.__dateTimeMinValueMillisecondsSinceEpoch = (DateTime.MinValue - ObjectId.__unixEpoch).Ticks / 10000L;
    ObjectId.__staticMachine = ObjectId.GetMachineHash();
    ObjectId.__staticIncrement = new Random().Next();
    ObjectId.__staticPid = (short) ObjectId.GetCurrentProcessId();
  }

  public ObjectId(byte[] bytes)
  {
    if (bytes == null)
      throw new ArgumentNullException(nameof (bytes));
    ObjectId.Unpack(bytes, out this._timestamp, out this._machine, out this._pid, out this._increment);
  }

  public ObjectId(DateTime timestamp, int machine, short pid, int increment)
    : this(ObjectId.GetTimestampFromDateTime(timestamp), machine, pid, increment)
  {
  }

  public ObjectId(int timestamp, int machine, short pid, int increment)
  {
    if (((long) machine & 4278190080L /*0xFF000000*/) != 0L)
      throw new ArgumentOutOfRangeException(nameof (machine), "The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
    if (((long) increment & 4278190080L /*0xFF000000*/) != 0L)
      throw new ArgumentOutOfRangeException(nameof (increment), "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
    this._timestamp = timestamp;
    this._machine = machine;
    this._pid = pid;
    this._increment = increment;
  }

  public ObjectId(string value)
  {
    if (value == null)
      throw new ArgumentNullException(nameof (value));
    ObjectId.Unpack(ObjectId.ParseHexString(value), out this._timestamp, out this._machine, out this._pid, out this._increment);
  }

  public static ObjectId Empty => ObjectId.__emptyInstance;

  public int Timestamp => this._timestamp;

  public int Machine => this._machine;

  public short Pid => this._pid;

  public int Increment => this._increment;

  public DateTime CreationTime => ObjectId.__unixEpoch.AddSeconds((double) this._timestamp);

  public static bool operator <(ObjectId lhs, ObjectId rhs) => lhs.CompareTo(rhs) < 0;

  public static bool operator <=(ObjectId lhs, ObjectId rhs) => lhs.CompareTo(rhs) <= 0;

  public static bool operator ==(ObjectId lhs, ObjectId rhs) => lhs.Equals(rhs);

  public static bool operator !=(ObjectId lhs, ObjectId rhs) => !(lhs == rhs);

  public static bool operator >=(ObjectId lhs, ObjectId rhs) => lhs.CompareTo(rhs) >= 0;

  public static bool operator >(ObjectId lhs, ObjectId rhs) => lhs.CompareTo(rhs) > 0;

  public static ObjectId GenerateNewId()
  {
    return ObjectId.GenerateNewId(ObjectId.GetTimestampFromDateTime(DateTime.UtcNow));
  }

  public static ObjectId GenerateNewId(DateTime timestamp)
  {
    return ObjectId.GenerateNewId(ObjectId.GetTimestampFromDateTime(timestamp));
  }

  public static ObjectId GenerateNewId(int timestamp)
  {
    int increment = Interlocked.Increment(ref ObjectId.__staticIncrement) & 16777215 /*0xFFFFFF*/;
    return new ObjectId(timestamp, ObjectId.__staticMachine, ObjectId.__staticPid, increment);
  }

  public static string GenerateNewStringId() => ObjectId.GenerateNewId().ToString();

  public static byte[] Pack(int timestamp, int machine, short pid, int increment)
  {
    if (((long) machine & 4278190080L /*0xFF000000*/) != 0L)
      throw new ArgumentOutOfRangeException(nameof (machine), "The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
    if (((long) increment & 4278190080L /*0xFF000000*/) != 0L)
      throw new ArgumentOutOfRangeException(nameof (increment), "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
    return new byte[12]
    {
      (byte) (timestamp >> 24),
      (byte) (timestamp >> 16 /*0x10*/),
      (byte) (timestamp >> 8),
      (byte) timestamp,
      (byte) (machine >> 16 /*0x10*/),
      (byte) (machine >> 8),
      (byte) machine,
      (byte) ((uint) pid >> 8),
      (byte) pid,
      (byte) (increment >> 16 /*0x10*/),
      (byte) (increment >> 8),
      (byte) increment
    };
  }

  public static ObjectId Parse(string s)
  {
    if (s == null)
      throw new ArgumentNullException(nameof (s));
    return s.Length == 24 ? new ObjectId(ObjectId.ParseHexString(s)) : throw new ArgumentOutOfRangeException(nameof (s), "ObjectId string value must be 24 characters.");
  }

  public static void Unpack(
    byte[] bytes,
    out int timestamp,
    out int machine,
    out short pid,
    out int increment)
  {
    if (bytes == null)
      throw new ArgumentNullException(nameof (bytes));
    if (bytes.Length != 12)
      throw new ArgumentOutOfRangeException(nameof (bytes), "Byte array must be 12 bytes long.");
    timestamp = ((int) bytes[0] << 24) + ((int) bytes[1] << 16 /*0x10*/) + ((int) bytes[2] << 8) + (int) bytes[3];
    machine = ((int) bytes[4] << 16 /*0x10*/) + ((int) bytes[5] << 8) + (int) bytes[6];
    pid = (short) (((int) bytes[7] << 8) + (int) bytes[8]);
    increment = ((int) bytes[9] << 16 /*0x10*/) + ((int) bytes[10] << 8) + (int) bytes[11];
  }

  [MethodImpl(MethodImplOptions.NoInlining)]
  private static int GetCurrentProcessId() => Process.GetCurrentProcess().Id;

  private static int GetMachineHash()
  {
    string machineName = Environment.MachineName;
    byte[] hash = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(machineName));
    return ((int) hash[0] << 16 /*0x10*/) + ((int) hash[1] << 8) + (int) hash[2];
  }

  private static int GetTimestampFromDateTime(DateTime timestamp)
  {
    return (int) Math.Floor((ObjectId.ToUniversalTime(timestamp) - ObjectId.__unixEpoch).TotalSeconds);
  }

  public int CompareTo(ObjectId other)
  {
    int num1 = this._timestamp.CompareTo(other._timestamp);
    if (num1 != 0)
      return num1;
    int num2 = this._machine.CompareTo(other._machine);
    if (num2 != 0)
      return num2;
    int num3 = this._pid.CompareTo(other._pid);
    return num3 != 0 ? num3 : this._increment.CompareTo(other._increment);
  }

  public bool Equals(ObjectId rhs)
  {
    return this._timestamp == rhs._timestamp && this._machine == rhs._machine && (int) this._pid == (int) rhs._pid && this._increment == rhs._increment;
  }

  public override bool Equals(object obj) => obj is ObjectId rhs && this.Equals(rhs);

  public override int GetHashCode()
  {
    return 37 * (37 * (37 * (37 * 17 + this._timestamp.GetHashCode()) + this._machine.GetHashCode()) + this._pid.GetHashCode()) + this._increment.GetHashCode();
  }

  public byte[] ToByteArray()
  {
    return ObjectId.Pack(this._timestamp, this._machine, this._pid, this._increment);
  }

  public override string ToString() => ObjectId.ToHexString(this.ToByteArray());

  public static byte[] ParseHexString(string s)
  {
    if (s == null)
      throw new ArgumentNullException(nameof (s));
    byte[] hexString = s.Length % 2 != 1 ? new byte[s.Length >> 1] : throw new Exception("The binary key cannot have an odd number of digits");
    for (int index = 0; index < s.Length >> 1; ++index)
      hexString[index] = (byte) ((ObjectId.GetHexVal(s[index << 1]) << 4) + ObjectId.GetHexVal(s[(index << 1) + 1]));
    return hexString;
  }

  public static string ToHexString(byte[] bytes)
  {
    char[] chArray = bytes != null ? new char[bytes.Length * 2] : throw new ArgumentNullException(nameof (bytes));
    for (int index = 0; index < bytes.Length; ++index)
    {
      uint num = ObjectId._lookup32[(int) bytes[index]];
      chArray[2 * index] = (char) num;
      chArray[2 * index + 1] = (char) (num >> 16 /*0x10*/);
    }
    return new string(chArray);
  }

  public static long ToMillisecondsSinceEpoch(DateTime dateTime)
  {
    return (ObjectId.ToUniversalTime(dateTime) - ObjectId.__unixEpoch).Ticks / 10000L;
  }

  public static DateTime ToUniversalTime(DateTime dateTime)
  {
    if (dateTime == DateTime.MinValue)
      return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
    return dateTime == DateTime.MaxValue ? DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc) : dateTime.ToUniversalTime();
  }

  private static int GetHexVal(char hex)
  {
    int num = (int) hex;
    return num - (num < 58 ? 48 /*0x30*/ : (num < 97 ? 55 : 87));
  }
}
